Skip to content

Conversation

@nerdeveloper
Copy link
Contributor

Summary

  • Adds documentation on creating custom markers for unsupported file extensions
  • Shows how to use Kubebuilder as a library for external plugins
  • Includes examples for Rust, Java, Python, and template files

Related

Test Plan

  • Documentation builds correctly
  • Code examples tested and compile without errors
  • Added to SUMMARY.md table of contents

@k8s-ci-robot k8s-ci-robot added the do-not-merge/invalid-commit-message Indicates that a PR should not merge because it has an invalid commit message. label Sep 22, 2025
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: nerdeveloper
Once this PR has been reviewed and has the lgtm label, please assign camilamacedo86 for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Sep 22, 2025
@k8s-ci-robot
Copy link
Contributor

Hi @nerdeveloper. Thanks for your PR.

I'm waiting for a kubernetes-sigs member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@nerdeveloper nerdeveloper force-pushed the docs/custom-markers-issue-4829 branch from ddb8cdb to 161acff Compare September 22, 2025 21:56
@k8s-ci-robot k8s-ci-robot removed the do-not-merge/invalid-commit-message Indicates that a PR should not merge because it has an invalid commit message. label Sep 22, 2025
- You're building an **external plugin** for a language not natively supported by Kubebuilder
- You want to scaffold files with custom extensions (`.rs`, `.java`, `.tpl`, `.py`, etc.)
- Your file extensions aren't (and shouldn't be) part of the core `commentsByExt` map
- You need to maintain scaffolding markers in non-Go files
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the documentation could be How to create Custom Markers.
For those who use Kubebuilder as a library, it would be possible to create their own custom markers, regardless of whether it is for an extension valid or not for Golang projects.

So, it would not only be valid for external references

builder.WriteString(":")
}
return builder.String()
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you let me know if you tested this example?
Can it work without any change in the code at this moment?
Is the code covered with tests to ensure that this example would be possible?

}
```

## Complete Example: Rust Plugin with Custom Markers
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need all those examples?
Could we not have only 1 example?
How could we simplify the doc and its maintainability?
Just to share what is required

Comment on lines 295 to 307
## Best Practices

1. **Use Clear Prefixes**: Choose a unique prefix for your plugin to avoid conflicts (e.g., `+rust:scaffold:`, `+java:scaffold:`)

2. **Handle Comments Correctly**: Different languages have different comment syntax. Make sure to map the correct comment style for each file extension.

3. **Error Handling**: Always validate file extensions and return clear error messages when unsupported files are encountered.

4. **Maintain Compatibility**: When possible, follow the same patterns as Kubebuilder's core marker system to maintain consistency.

5. **Document Your Markers**: Clearly document what markers your plugin supports and where they should be placed.

6. **Testing**: Test your marker implementation with various file types and edge cases.
Copy link
Member

@camilamacedo86 camilamacedo86 Sep 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Best Practices is something subjective, I think it's better to avoid this term.

}
```

## Conclusion
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please review the other documentation we have?
We do not use a conclusion. We should always follow the same layout and standards.

@nerdeveloper
Copy link
Contributor Author

Hi @camilamacedo86,

Thank you for the review feedback! I've updated the documentation to address your comments:

✅ Changed title to "Creating Custom Markers" (broader scope, not just for unsupported extensions)
✅ Simplified to only ONE example (Rust) instead of multiple examples
✅ Removed "Best Practices" section (subjective term)
✅ Removed "Conclusion" section (to match other docs)
✅ Made the documentation more concise and maintainable

Regarding testing: I performed minimal testing of the marker generation logic by creating a standalone Go program that tests the core marker creation code. The test confirms:

  • The code compiles without errors
  • Markers are generated with correct comment syntax for different file types (e.g., // +rust:scaffold:imports for Rust, # +python:scaffold:functions for Python)
  • The marker format is correct

However, I have NOT tested the complete integration with external plugins end-to-end (the full example with templates and plugin integration).

Question: What would you prefer for this documentation?

  1. Keep the current example as a pattern/reference (with a note that it's not fully integration-tested)
  2. Simplify further to only show the minimal testable code (just the marker creation)
  3. Add a working example to an external test repository and link to it

Please let me know your preference, and I'll update accordingly!

@nerdeveloper nerdeveloper force-pushed the docs/custom-markers-issue-4829 branch from 161acff to 51faf4e Compare September 27, 2025 10:39
Comment on lines 160 to 168
## Key Considerations

1. **Unique Prefixes**: Choose a unique prefix for your plugin to avoid conflicts (e.g., `+rust:scaffold:`, `+java:scaffold:`)

2. **Comment Syntax**: Different languages have different comment syntax. Ensure you map the correct comment style for each file extension

3. **Error Handling**: Validate file extensions and return clear error messages for unsupported files

4. **Testing**: Test your marker implementation with various scenarios to ensure reliability
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all is great !!!
Well done 👍 Good job but wdyt about we remove this part?
I think is best we avoid adding things that are more opinionated and keep it as low verbose as possible.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds comprehensive documentation for creating custom markers in Kubebuilder when working with unsupported file extensions. It demonstrates how to use Kubebuilder as a library for external plugins, enabling support for languages beyond Go (such as Rust, Java, and Python) by implementing custom marker systems.

Key Changes:

  • Adds complete guide for implementing custom markers for non-Go file extensions
  • Provides working code examples for Rust-based external plugins
  • Includes integration examples showing external plugin communication patterns

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
docs/book/src/plugins/extending/custom-markers.md New documentation file explaining custom marker implementation with Rust examples and external plugin integration
docs/book/src/SUMMARY.md Adds table of contents entry for the new custom markers documentation

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 159 to 162
case "flags":
response = handleFlags(pluginRequest)
case "metadata":
response = handleMetadata(pluginRequest)
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example references handleFlags and handleMetadata functions that are not defined or shown in the documentation. Either provide these function implementations or remove these case statements to avoid confusion for readers trying to follow the example.

Suggested change
case "flags":
response = handleFlags(pluginRequest)
case "metadata":
response = handleMetadata(pluginRequest)

Copilot uses AI. Check for mistakes.

func handleInit(req *external.PluginRequest) external.PluginResponse {
// Create Rust file with custom markers
marker, _ := markers.NewRustMarker("src/main.rs", "imports")
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error is silently discarded using blank identifier. In documentation examples, it's better to demonstrate proper error handling to avoid teaching bad practices. Consider handling the error or adding a comment explaining why it's safe to ignore in this specific context.

Suggested change
marker, _ := markers.NewRustMarker("src/main.rs", "imports")
marker, err := markers.NewRustMarker("src/main.rs", "imports")
if err != nil {
return external.PluginResponse{
Command: "init",
Error: true,
ErrorMsgs: []string{fmt.Sprintf("failed to create Rust marker: %v", err)},
}
}

Copilot uses AI. Check for mistakes.
@nerdeveloper
Copy link
Contributor Author

nerdeveloper commented Oct 27, 2025

How to Test This Implementation

@camilamacedo86 Here are step-by-step instructions to test the custom marker implementation:

Quick Test

1. Test the core marker functionality:

# Create a test file in any directory
cat > test_markers.go << 'EOF'
package main

import (
	"fmt"
	"path/filepath"
	"strings"
)

const RustPluginPrefix = "+rust:scaffold:"

type RustMarker struct {
	prefix  string
	comment string
	value   string
}

func NewRustMarker(path string, value string) (RustMarker, error) {
	ext := filepath.Ext(path)
	if ext != ".rs" {
		return RustMarker{}, fmt.Errorf("expected .rs file, got %s", ext)
	}
	return RustMarker{
		prefix:  formatPrefix(RustPluginPrefix),
		comment: "//",
		value:   value,
	}, nil
}

func (m RustMarker) String() string {
	return m.comment + " " + m.prefix + m.value
}

func formatPrefix(prefix string) string {
	trimmed := strings.TrimSpace(prefix)
	var builder strings.Builder
	if !strings.HasPrefix(trimmed, "+") {
		builder.WriteString("+")
	}
	builder.WriteString(trimmed)
	if !strings.HasSuffix(trimmed, ":") {
		builder.WriteString(":")
	}
	return builder.String()
}

func main() {
	fmt.Println("Testing marker creation...")
	marker, err := NewRustMarker("src/main.rs", "imports")
	if err != nil {
		fmt.Printf("Error: %v", err)
		return
	}
	fmt.Printf("Generated marker: %s", marker.String())
	
	// Test error case
	_, err = NewRustMarker("main.go", "imports")
	if err != nil {
		fmt.Printf("\nCorrect error for Go file: %v", err)
	}
}
EOF

# Run the test
go run test_markers.go

Expected output:

Testing marker creation...
Generated marker: // +rust:scaffold:imports
Correct error for Go file: expected .rs file, got .go

Verify External Plugin Imports

2. Test that external plugin types are accessible:

cat > test_external.go << 'EOF'
package main

import (
	"fmt"
	"sigs.k8s.io/kubebuilder/v4/pkg/plugin/external"
)

func main() {
	var req external.PluginRequest
	var resp external.PluginResponse
	fmt.Printf("PluginRequest available: %T\n", req)
	fmt.Printf("PluginResponse available: %T\n", resp)
	fmt.Println("External plugin imports working!")
}
EOF

go run test_external.go

Expected output:

PluginRequest available: external.PluginRequest
PluginResponse available: external.PluginResponse
External plugin imports working!

Clean Up

rm test_markers.go test_external.go

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@nerdeveloper
Copy link
Contributor Author

when all is good, i wil squash all of them into one commit

@camilamacedo86
Copy link
Member

camilamacedo86 commented Nov 2, 2025

@nerdeveloper

Could you please:

  • Run make remove-spaces to fix the check.
  • And squash the commits I think it is good to get merged

Thank you a lot for your contribution 🎉

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}
`, marker.String())

// External plugins use "universe" to represent file changes
Copy link

Copilot AI Nov 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] While there's a comment explaining that 'External plugins use "universe" to represent file changes', it would be helpful to add more context about what 'universe' represents and why it's named as such. Consider expanding the comment to explain that it's a map of file paths to their contents that gets passed through the plugin chain.

Suggested change
// External plugins use "universe" to represent file changes
// External plugins use "universe" to represent file changes.
// "universe" is a map from file paths (as strings) to their file contents (also strings).
// This map is passed through the plugin chain to communicate which files should be created or updated,
// allowing plugins to coordinate file generation and modifications.

Copilot uses AI. Check for mistakes.
@nerdeveloper
Copy link
Contributor Author

@camilamacedo86 do you want me to also implement copilot suggestions on this pr.

@camilamacedo86
Copy link
Member

Hi @nerdeveloper

For we get this merged we need to:

  • Run make remove-spaces
  • Squash the commits
  • And the reviews that make sense from copilot; can you please address?

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +66 to +77
func formatPrefix(prefix string) string {
trimmed := strings.TrimSpace(prefix)
var builder strings.Builder
if !strings.HasPrefix(trimmed, "+") {
builder.WriteString("+")
}
builder.WriteString(trimmed)
if !strings.HasSuffix(trimmed, ":") {
builder.WriteString(":")
}
return builder.String()
}
Copy link

Copilot AI Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The formatPrefix function duplicates logic from Kubebuilder's internal markerPrefix function in pkg/machinery/marker.go. Consider mentioning in the documentation that this is adapted from Kubebuilder's internal implementation, or reference the actual implementation pattern to help maintainers understand the source of this logic.

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nerdeveloper I think it is a nice review

Comment on lines 167 to 168
output, _ := json.Marshal(response)
fmt.Printf("%s", output)
Copy link

Copilot AI Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error from json.Marshal is silently ignored with blank identifier. While this is unlikely to fail for the given struct, this demonstrates a pattern that could be problematic if the response structure becomes more complex. Consider logging or handling the error, especially since this is example code that developers will likely copy.

Copilot uses AI. Check for mistakes.
Shows how to use Kubebuilder as a library to create custom marker support
for external plugins with non-Go file extensions like .rs, .java, .py

- Adds comprehensive guide for implementing custom markers
- Provides working code examples for Rust-based external plugins
- Includes integration patterns for external plugin communication
- Addresses all PR review feedback and Copilot suggestions
- Fixes trailing whitespace and code quality issues

Fixes kubernetes-sigs#4829
@nerdeveloper nerdeveloper force-pushed the docs/custom-markers-issue-4829 branch from 40f6ec5 to c51a0a6 Compare November 10, 2025 03:31
@k8s-ci-robot k8s-ci-robot added the do-not-merge/invalid-commit-message Indicates that a PR should not merge because it has an invalid commit message. label Nov 10, 2025
@k8s-ci-robot
Copy link
Contributor

Keywords which can automatically close issues and at(@) or hashtag(#) mentions are not allowed in commit messages.

The list of commits with invalid commit messages:

  • c51a0a6 📖 Add docs: Creating custom markers for unsupported file extensions

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@nerdeveloper
Copy link
Contributor Author

nerdeveloper commented Nov 10, 2025

@camilamacedo86 All requested changes have been completed:

Trailing spaces removed - Ran make remove-spaces successfully
Commits squashed - All 5 commits consolidated into 1 meaningful commit
Copilot suggestions addressed:

  • Added note about formatPrefix being adapted from Kubebuilder's internal implementation
  • Added proper error handling for json.Marshal calls (2 instances)
  • Expanded universe comment to explain its purpose and usage

return builder.String()
}

// Note: This formatPrefix implementation is adapted from Kubebuilder's internal
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have a class for note.
Could you please check it out?
Example;

<aside class="note warning">
<h1>Automation process will involve deleting all files to regenerate</h1>
Deletes all files except `.git` and `PROJECT`.
</aside>

Also, remove from the block of code and link the Kubebuilder implementation?

- [helm/v1-alpha](./plugins/available/helm-v1-alpha.md)
- [helm/v2-alpha](./plugins/available/helm-v2-alpha.md)
- [kustomize/v2](./plugins/available/kustomize-v2.md)
- [Extending](./plugins/extending.md)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that the commit has been marked as invalid.
Could you please check if it contains an invalid character or something similar?

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

"fmt"
"io"
"os"
"path/filepath"
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The path/filepath import on line 135 is unused in this example. It should be removed to avoid confusion, as filepath is only imported but never used in the main function or any of its helper functions.

Suggested change
"path/filepath"

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. do-not-merge/invalid-commit-message Indicates that a PR should not merge because it has an invalid commit message. needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add docs: how to create custom markers for unsupported file extensions

3 participants